package org.raymond.iworks.study.netty.nio;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @author raymond
 * @version V1.0
 * @Description: TODO
 */

/**
 * 边缘触发(edge-triggered)ET: 每当状态变化时,触发一个事件.
 * 水平触发(level-triggered,也被称为条件触发)LT: 只要满足条件,就触发一个事件(只要有数据没有被获取,内核就不断通知你)
 * a) 对于读操作: 只要内核缓冲区内容不为空，LT模式返回读就绪。
 * b) 对于写操作: 只要内核缓冲区还不满，LT模式会返回写就绪。
 * Java的NIO属于水平触发,即条件触发.
 *
 * 水平触发和条件触发在IO编程的区别:
 * 举个读socket的例子,假定经过长时间的沉默后,现在来了100个字节,这时无论边缘触发和条件触发都会产生一个read ready notification通知
 * 应用程序可读.应用程序读了50个字节,然后重新调用API等待io事件.这时条件触发的api会因为还有50个字节可读从而立即返回用户一个
 * read ready notification.而边缘触发的api会因为可读这个状态没有发生变化而陷入长期等待.
 * 因此在使用边缘触发的api时,要注意每次都要读到socket返回EWOULDBLOCK为止,否则这个socket就算废了.
 * 而使用条件触发的API时,如果应用程序不需要写就不要关注socket可写的事件,否则就会无限次的立即返回一个write ready notification.
 * 大家常用的select就是属于条件触发这一类,长期关注socket写事件会出现CPU 100%的毛病.所以在使用Java的NIO编程的时候,
 * 在没有数据可以往外写的时候要取消写事件,在有数据往外写的时候再注册写事件.
 *
 * 水平触发：

 *
 */
public class NIOClient {
    public static void main(String[] args) throws Exception {
        SocketChannel socketChannel = SocketChannel.open();
        InetSocketAddress address = new InetSocketAddress("127.0.0.1", 12000);
        socketChannel.configureBlocking(false);
        //连接服务器
        boolean connect = socketChannel.connect(address);
        // 判断连接是否成功
        if(!connect){
            // 等待连接中
            while(!socketChannel.finishConnect()){
                System.out.println("等待连接中,可以做其他事");
            }
        }
        String msg = "学习NIO: bytebuffer!";
        ByteBuffer byteBuffer = ByteBuffer.wrap(msg.getBytes());
        // 把bytebuffer写入通道中
        socketChannel.write(byteBuffer);
        /*
        byteBuffer.flip();

        Selector selector = Selector.open();
        // 连接就绪事件,针对客户端
        // SelectionKey.OP_CONNECT
        // 写就绪事件
        // SelectionKey.OP_WRITE
        socketChannel.register(selector, SelectionKey.OP_READ);

        boolean running = true;
        while (running){
            if(selector.select(3000)==0){
                System.out.println("客户端等待了3秒,没有等到响应");
            }
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> it = selectedKeys.iterator();
            while (it.hasNext()){
                SelectionKey key = it.next();
                if(key.isReadable()){
                    SocketChannel receiveSc = (SocketChannel)key.channel();
                    int read = receiveSc.read(byteBuffer);
                    // 读取到响应数据
                    if(read>0) {
                        byteBuffer.flip();
                        System.out.println("服务端响应的数据:" + new String(byteBuffer.array()).trim());
                        // 对端关闭连接
                    }else if(read==-1) {
                        receiveSc.close();
                        running = false;
                    }
                }
                it.remove();
            }
        }
        */
    }
}
